package mule.view;

//<editor-fold defaultstate="collapsed" desc="Imports">
import java.util.Timer;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.geometry.Rectangle2DBuilder;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.SceneBuilder;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBuilder;
import javafx.scene.control.Label;
import javafx.scene.control.LabelBuilder;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextAreaBuilder;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.ImageViewBuilder;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPaneBuilder;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.GridPaneBuilder;
import javafx.scene.layout.HBox;
import javafx.scene.layout.HBoxBuilder;
import javafx.scene.layout.VBox;
import javafx.scene.layout.VBoxBuilder;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.text.TextBuilder;
import javafx.stage.Popup;
import javafx.stage.PopupBuilder;
import javafx.stage.Stage;
import javafx.util.Duration;
import mule.model.Player;
import mule.model.Tile;
import mule.model.enums.GameStage;
import mule.model.enums.Race;
//</editor-fold>

/**
 * Creates the view that the player sees when playing the game.
 *
 * @author stephen
 */
public class GameView extends View
{
    private static final Integer STARTTIME = 30;
    public IntegerProperty timeSeconds;
    public Timeline timeline;
    private Timer timer;
    private StringProperty informationText;
    private ObjectProperty<GameStage> gameStage;
    private Text stageStatus;
    private Popup townPopup;
    private Popup storePopup;
    private Popup pubPopup;
    private Popup resourceStorePopup;
    private Popup resourceStoreBuyPopup;
    private Popup resourceStoreSellPopup;
    private Button gamble;
    private Button buyMule;
    private Button foodFactory;
    private Button energyFactory;
    private Button oreFactory;
    private Button buyFood;
    private Button buyEnergy;
    private Button buyOre;
    private Button sellFood;
    private Button sellEnergy;
    private Button sellOre;
    private Button passTurn;

    private ObjectProperty<Player> currentPlayer;
    private StringProperty playerName;
    private IntegerProperty playerFoodCount;
    private IntegerProperty playerEnergyCount;
    private IntegerProperty playerOreCount;
    private IntegerProperty playerMoney;
    private ObjectProperty<Race> playerRace;
    private ObjectProperty<Color> playerColor;

    private IntegerProperty storeFoodCount;
    private IntegerProperty storeEnergyCount;
    private IntegerProperty storeOreCount;
    private IntegerProperty storeMuleCount;
    private ObjectProperty<Tile>[][] tileMap;
    private Stage stage;
    
    /**
     * Constructs the gameView and initializes all of the properties we need.
     * 
     * @param tileMap the map to create for the gridPane
     * @param stage the stage to draw onto
     */
    public GameView( ObjectProperty<Tile>[][] tileMap, Stage stage )
    {
        timeSeconds = new SimpleIntegerProperty( STARTTIME * 100 );
        timeline = new Timeline();
        timer = new Timer();
        playerName = new SimpleStringProperty( "Default" );
        playerFoodCount = new SimpleIntegerProperty( 0 );
        playerEnergyCount = new SimpleIntegerProperty( 0 );
        playerOreCount = new SimpleIntegerProperty( 0 );
        playerMoney = new SimpleIntegerProperty( 0 );
        playerRace = new SimpleObjectProperty( null );
        playerColor = new SimpleObjectProperty( null );
        informationText = new SimpleStringProperty( "Default" );
        gameStage = new SimpleObjectProperty( GameStage.PropertySelection );
        storeFoodCount = new SimpleIntegerProperty( 0 );
        storeEnergyCount = new SimpleIntegerProperty( 0 );
        storeOreCount = new SimpleIntegerProperty( 0 );
        storeMuleCount = new SimpleIntegerProperty( 0 );
        townPopup = townPopup();
        pubPopup = pubPopup();
        storePopup = storePopup();
        resourceStorePopup = resourceStorePopup();
        resourceStoreBuyPopup = resourceStoreBuyPopup();
        resourceStoreSellPopup = resourceStoreSellPopup();
        this.tileMap = tileMap;
        this.stage = stage;
    }

    /**
     * Returns the screen to show to the player.
     * 
     * @return a scene with the game screen drawn onto it. 
     */
    public Scene getScreen()
    {
        return createGameScreen();
    }

    /**
     * Creates the scene that the player will see when playing the game.
     * 
     * @return the game screen scene 
     */
    private Scene createGameScreen()
    {
        Scene gameScreen = SceneBuilder.create()
            .width(SCREEN_WIDTH)
            .height(SCREEN_HEIGHT)
            .root(
                BorderPaneBuilder.create()
                .top(
                    gameStageStatus()
                )
                .center(
                    mapGrid() 
                )
                .bottom(
                    playerStatPane()
                )
                .build()
            )
        .build();

        return gameScreen;
    }

    /**
     * Creates the top panel with progress, the game stage, and the timer label
     * 
     * @return an HBox containing the above information 
     */
    private Node gameStageStatus()
    {
        Label timerLabel = LabelBuilder.create()
        .build();
        timerLabel.textProperty().bind( timeSeconds.divide( 100 ).asString() );

        ProgressBar stageProgress = new ProgressBar();
        stageProgress.prefWidth(200);
        stageProgress.progressProperty().bind( 
            timeSeconds.divide(STARTTIME*100.0));

        timeSeconds.set( ( STARTTIME + 1 ) * 100 );
        timeline.getKeyFrames()
            .add(
                new KeyFrame( Duration.seconds(STARTTIME + 1 ),
                new KeyValue( timeSeconds, 0 ) )
            );
        timeline.playFromStart();

        stageStatus = TextBuilder.create()
            .text( gameStage.get().toString() )
        .build();
        
        VBox gameStageStatus = VBoxBuilder.create()
            .alignment( Pos.CENTER )
            .children(
                timerLabel,
                stageProgress,
                stageStatus
            )
        .build();

        passTurn = ButtonBuilder.create()
            .text("Pass")
        .build();

        HBox gameStageStatusBox = HBoxBuilder.create()
            .alignment(Pos.CENTER)
            .spacing( 20 )
            .children(
                VBoxBuilder.create()
                    .alignment( Pos.CENTER )
                    .children(
                        timerLabel,
                        stageProgress,
                        stageStatus
                    )
                    .build(),
                passTurn
            )
        .build();
        
        return gameStageStatusBox;
    }


    /**
     * Creates the gridpane that represents the map
     * 
     * @return a gridpane full of MapTiles
     */
    private Node mapGrid()
    {
        GridPane map = GridPaneBuilder.create()
            .alignment( Pos.CENTER )
            .prefWidth(SCREEN_WIDTH)
        .build();

        for( int i = 0; i < tileMap.length; i++ )
        {
            for( int j = 0; j < tileMap[i].length; j++)
            {
                MapTile tile = new MapTile( tileMap[i][j] );
                map.add( tile, j, i );
                if( tileMap[i][j].get().getName().equals( "town" ) )
                {
                    tile.addEventHandler( MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>()
                    {
                        public void handle( MouseEvent t )
                        {
                            if( gameStage.get().equals( GameStage.Developement ) )
                            {
                                if( !storePopup.isShowing() && !pubPopup.isShowing() )
                                {
                                    townPopup.show( stage );
                                }
                            }
                            else if( gameStage.get().equals( GameStage.Store ) )
                            {
                                if( !resourceStoreBuyPopup.isShowing() && !resourceStoreSellPopup.isShowing() )
                                {
                                    resourceStorePopup.show( stage );
                                }
                            }
                        }
                    });
                }
            }
        }

        return map;
    }

    /**
     * Creates the statistics for the player
     * 
     * @return a node containing player statistics 
     */
    private Node playerStatPane()
    {
        HBox playerStatPane = HBoxBuilder.create()
            .alignment( Pos.CENTER_LEFT )
            .prefHeight( 200 )
            .prefWidth( SCREEN_WIDTH )
            .children(
                playerPicturePane(),
                playerInventoryPane(),
                messageAndMoneyPane(),
                storeInventoryPane()
            )
        .build();

        return playerStatPane;
    }

    /**
     * Creates the player picture
     * 
     * @return a node containing the picture for the player
     */
    private Node playerPicturePane()
    {
        Label playerNameLabel = LabelBuilder.create()
        .build();
        playerNameLabel.textProperty().bind( playerName );

        Rectangle2D playerPictureViewport = Rectangle2DBuilder.create()
        .build();

        ImageView playerImageView = ImageViewBuilder.create()
            .viewport( playerPictureViewport )
            .image( new Image( "mule/view/images/" + playerRace.get().toString().toLowerCase() + "_image.png" ) )
        .build();

        VBox playerPicturePane = VBoxBuilder.create()
            .alignment( Pos.CENTER )
            .children(
                playerNameLabel,
                playerImageView
            )
        .build();

        return playerPicturePane;
    }

    /**
     * Creates the node for the player's inventory
     * 
     * @return a node containing counts for the items in the player's inventory
     */
    private Node playerInventoryPane()
    {
        Label playerInventoryLabel = LabelBuilder.create()
            .text( "Player Inventory" )
        .build();

        Label foodLabel = LabelBuilder.create()
            .alignment( Pos.CENTER )
        .build();
        foodLabel.textProperty().bind( playerFoodCount.asString().concat( "Food" ) );

        Label energyLabel = LabelBuilder.create()
            .alignment( Pos.CENTER )
        .build();
        energyLabel.textProperty().bind( playerEnergyCount.asString().concat( " Energy" ) );

        Label oreLabel = LabelBuilder.create()
            .alignment( Pos.CENTER )
        .build();
        oreLabel.textProperty().bind( playerOreCount.asString().concat( " Ore" ) );

        GridPane playerInventoryGrid = new GridPane();
        playerInventoryGrid.add( foodLabel,   0, 0);
        playerInventoryGrid.add( energyLabel, 1, 0);
        playerInventoryGrid.add( oreLabel,    0, 1);

        VBox playerInventoryPane = VBoxBuilder.create()
            .alignment(Pos.CENTER)
            .children(
                playerInventoryLabel,
                playerInventoryGrid
            )
        .build();

        return playerInventoryPane;
    }

    /**
     * Creates a panel with player money and the information text about
     * events that are happening in the game
     * 
     * @return a node containing player information and money 
     */
    private Node messageAndMoneyPane()
    {
        Label messageLabel = LabelBuilder.create()
            .text( "Messages: ")
        .build();

        TextArea messageTextArea = TextAreaBuilder.create()
            .wrapText(true)
            .editable(false)
            .mouseTransparent(false)
        .build();
        messageTextArea.textProperty().bind( informationText );

        Label moneyLabel = LabelBuilder.create()
        .build();
        moneyLabel.textProperty().bind( playerMoney.asString().concat( " money") );

        VBox messageAndMoneyPane = VBoxBuilder.create()
            .alignment( Pos.CENTER )
            .children(
                messageLabel,
                messageTextArea,
                moneyLabel
            )
        .build();

        return messageAndMoneyPane;
    }


    /**
     * Creates the popup for the town screen
     * 
     * @return the town popup 
     */
    private Popup townPopup()
    {
        final Popup myPopup;
        Label townTitle = new Label( "Town" );
        Button store = new Button( "Store" );
        Button pub = new Button( "Pub" );

        HBox buttons = HBoxBuilder.create()
            .alignment(Pos.CENTER)
            .children(
                store,
                pub
            )
        .build();

        VBox contentItems = VBoxBuilder.create()
            .alignment(Pos.CENTER)
            .children(
                townTitle,
                buttons
            )
        .build();

        myPopup = PopupBuilder.create()
            .content(
                contentItems
            )
        .build();
        
        store.onMouseClickedProperty().set( new EventHandler<MouseEvent>()
        {
            @Override
            public void handle( MouseEvent event )
            {
                myPopup.hide();
                storePopup.show( stage );
            }
        });
        
        pub.onMouseClickedProperty().set( new EventHandler<MouseEvent>()
        {
            @Override
            public void handle( MouseEvent event )
            {
                myPopup.hide();
                pubPopup.show( stage );
            }
        });

        return myPopup;
    }

    /**
     * Creates the popup for the store
     * 
     * @return the store popup 
     */
    private Popup storePopup()
    {
        buyMule = ButtonBuilder.create()
            .text( "Buy Mule" )
        .build();

        foodFactory = ButtonBuilder.create()
            .text( "Food Factory" )
        .build();

        energyFactory = ButtonBuilder.create()
            .text( "Energy Factory" )
        .build();

        oreFactory = ButtonBuilder.create()
            .text( "Ore Factory" )
        .build();

        return PopupBuilder.create()
            .content(
                VBoxBuilder.create()
                    .children(
                        HBoxBuilder.create()
                            .children(
                                buyMule
                            )
                        .build(),
                        HBoxBuilder.create()
                            .children(
                                foodFactory,
                                energyFactory
                            )
                        .build(),
                        HBoxBuilder.create()
                            .children(
                                oreFactory
                            )
                        .build()
                    )
                .build()
            )
        .build();
    }

    /**
     * Creates the popup that is seen when clicking on the pub
     * 
     * @return a popup with pub options
     */
    private Popup pubPopup()
    {
        gamble = ButtonBuilder.create()
            .text("Gamble")
        .build();

        gamble.onActionProperty().set(null);

        return PopupBuilder.create()
            .content(
                gamble
            )
        .build();
    }

    /**
     * Creates a popup with the store purchase options
     * 
     * @return a popup with options to buy or sell resources from the store 
     */
    private Popup resourceStorePopup()
    {
        Button buy = ButtonBuilder.create()
            .text( "Buy" )
        .build();

        Button sell = ButtonBuilder.create()
            .text( "Sell" )
        .build();

        Popup myPopup = PopupBuilder.create()
            .content(
                HBoxBuilder.create()
                    .children(
                        buy,
                        sell
                    )
                .build()
            )
        .build();

        buy.onMouseClickedProperty().set( new EventHandler<MouseEvent>()
        {
            @Override
            public void handle( MouseEvent event )
            {
                resourceStorePopup.hide();
                resourceStoreBuyPopup.show( stage );
            }
        });

        sell.onMouseClickedProperty().set( new EventHandler<MouseEvent>()
        {
            @Override
            public void handle( MouseEvent event )
            {
                resourceStorePopup.hide();
                resourceStoreSellPopup.show( stage );
            }
        });

        return myPopup;
    }

    /**
     * Provides buying options for the store
     * 
     * @return a popup of things to buy in the store 
     */
    private Popup resourceStoreBuyPopup()
    {
        buyFood = ButtonBuilder.create()
            .text( "Buy Food" )
        .build();

        buyEnergy = ButtonBuilder.create()
            .text( "Buy Energy" )
        .build();

        buyOre = ButtonBuilder.create()
            .text( "Buy Ore" )
        .build();

        Popup myPopup = PopupBuilder.create()
            .content(
                HBoxBuilder.create()
                    .children(
                        buyFood,
                        buyEnergy,
                        buyOre
                    )
                .build()
            )
        .build();

        return myPopup;

    }

    /**
     * Provides a selling screen for the store
     * 
     * @return a list of things that the store can sell. 
     */
    private Popup resourceStoreSellPopup()
    {
        sellFood = ButtonBuilder.create()
            .text( "Sell Food" )
        .build();

        sellEnergy = ButtonBuilder.create()
            .text( "Sell Energy" )
        .build();

        sellOre = ButtonBuilder.create()
            .text( "Sell Ore" )
        .build();

        Popup myPopup = PopupBuilder.create()
            .content(
                HBoxBuilder.create()
                    .children(
                        sellFood,
                        sellEnergy,
                        sellOre
                    )
                .build()
            )
        .build();

        return myPopup;
    }

    private Node storeInventoryPane()
    {
        Label storeInventoryLabel = LabelBuilder.create()
            .text( "Store Inventory" )
        .build();

        Label foodLabel = LabelBuilder.create()
            .alignment( Pos.CENTER )
        .build();
        foodLabel.textProperty().bind( storeFoodCount.asString().concat( " Food" ) );

        Label energyLabel = LabelBuilder.create()
            .alignment( Pos.CENTER )
        .build();
        energyLabel.textProperty().bind( storeEnergyCount.asString().concat( " Energy" ) );

        Label oreLabel = LabelBuilder.create()
            .alignment( Pos.CENTER )
        .build();
        oreLabel.textProperty().bind( storeOreCount.asString().concat( " Ore" ) );

        Label muleLabel = LabelBuilder.create()
            .alignment(Pos.CENTER)
        .build();
        muleLabel.textProperty().bind( storeMuleCount.asString().concat( " Mules" ) );

        GridPane storeInventoryGrid = new GridPane();
        storeInventoryGrid.add( foodLabel,   0, 0);
        storeInventoryGrid.add( energyLabel, 1, 0);
        storeInventoryGrid.add( oreLabel,    0, 1);
        storeInventoryGrid.add( muleLabel,   1, 1);

        VBox storeInventoryPane = VBoxBuilder.create()
            .alignment(Pos.CENTER)
            .children(
                storeInventoryLabel,
                storeInventoryGrid
            )
        .build();

        return storeInventoryPane;
    }

    public void killPopups()
    {
        townPopup.hide();
        pubPopup.hide();
        storePopup.hide();
        resourceStoreBuyPopup.hide();
        resourceStoreSellPopup.hide();
        resourceStorePopup.hide();
    }


    //BINDINGS

    public void bindCurrentPlayer( ObjectProperty<Player> currentPlayer )
    {
        this.currentPlayer = currentPlayer;
        
        addPlayerBindings();
        playerName.set( currentPlayer.get().getName() );
        playerRace.set( Race.valueOf( currentPlayer.get().getRace() ) );
        playerColor.set( currentPlayer.get().getColor() );
      /*playerMoney.set( currentPlayer.get().getMoney() );
        playerFoodCount.set( currentPlayer.get().getFood() );
        playerEnergyCount.set( currentPlayer.get().getEnergy() );
        playerOreCount.set( currentPlayer.get().getOre() );
      */
       
 
        this.currentPlayer.addListener( new ChangeListener<Player>()
        {
            @Override
            public void changed( ObservableValue<? extends Player> obsValue, Player oldPlayer, Player newPlayer)
            {
                if( gameStage.getValue() == GameStage.PropertySelection ){                    
                    newPlayer.produce();                                        
                }
                
                addPlayerBindings();               
                playerName.set( newPlayer.getName() );
                playerRace.set( Race.valueOf( newPlayer.getRace() ) );
                playerColor.set( newPlayer.getColor() );
              /*playerMoney.set( newPlayer.getMoney() );
                playerFoodCount.set( newPlayer.getFood() );
                playerEnergyCount.set( newPlayer.getEnergy() );
                playerOreCount.set( newPlayer.getOre() );
              */
                
                timeline.stop();
                timeSeconds.set( ( STARTTIME + 1 ) * 100 );
                timeline.playFromStart();
            }
        });
    }
    
    private void addPlayerBindings()
    {
        playerMoney.bind(currentPlayer.get().moneyProperty());
        playerFoodCount.bind(currentPlayer.get().foodProperty());
        playerEnergyCount.bind(currentPlayer.get().energyProperty());
        playerOreCount.bind(currentPlayer.get().oreProperty());
    }
    
    public void bindInformationText( StringProperty informationText )
    {
        this.informationText.bind( informationText );
    }

    public void bindStoreFoodCount( IntegerProperty foodCount )
    {
        storeFoodCount.bind( foodCount );
    }

    public void bindStoreEnergyCount( IntegerProperty energyCount )
    {
        storeEnergyCount.bind( energyCount );
    }

    public void bindStoreOreCount( IntegerProperty oreCount )
    {
        storeOreCount.bind( oreCount );
    }

    public void bindStoreMuleCount( IntegerProperty muleCount )
    {
        storeMuleCount.bind( muleCount );
    }

    public void bindGameStage( ObjectProperty<GameStage> stage )
    {
        gameStage.bind( stage );
        this.gameStage.addListener( new ChangeListener<GameStage>()
        {
            @Override
            public void changed( ObservableValue<? extends GameStage> obsValue, GameStage oldStage, GameStage newStage)
            {
                stageStatus.setText( newStage.toString() );
            }
        });
    }

    // LISTENERS
    public void addTimerFinishedListener( EventHandler event ) 
    {
        timeline.onFinishedProperty().set( event );
    }

    public void addBuyMuleListener( EventHandler event )
    {
        buyMule.onActionProperty().set( event );
    }

    public void addBuyFoodFactoryListener( EventHandler event )
    {
        foodFactory.onActionProperty().set( event );
    }
    
    public void addBuyEnergyFactoryListener( EventHandler event )
    {
        energyFactory.onActionProperty().set( event );
    }

    public void addBuyOreFactoryListener( EventHandler event )
    {
        oreFactory.onActionProperty().set( event );
    }

    public void addGambleListener( EventHandler event )
    {
        gamble.onActionProperty().set( event );
    }

    public void addBuyFoodListener( EventHandler event )
    {
        buyFood.onActionProperty().set( event );
    }

    public void addBuyEnergyListener( EventHandler event )
    {
        buyEnergy.onActionProperty().set( event );
    }

    public void addBuyOreListener( EventHandler event )
    {
        buyOre.onActionProperty().set( event );
    }

    public void addSellFoodListener( EventHandler event )
    {
        sellFood.onActionProperty().set( event );
    }

    public void addSellEnergyListener( EventHandler event )
    {
        sellEnergy.onActionProperty().set( event );
    }

    public void addSellOreListener( EventHandler event )
    {
        sellOre.onActionProperty().set( event );
    }

    public void addPassTurnListener( EventHandler event )
    {
        passTurn.onActionProperty().set( event );
    }
    
    //<editor-fold defaultstate="collapsed" desc="Methods for Testing">
    public Button getPassButtonForTesting()
    {
        return passTurn;
    }
    //</editor-fold>
}
